/**
 * Copyright 2011 Honeywell Federal Manufacturing & Technologies, LLC. This material was produced under U.S. Government
 * contract DE-ACO4-01AL66850, Honeywell Federal Manufacturing & Technologies, LLC, (FM&T) with the U. S. Department of
 * Energy for the operation of the Kansas City Plant . The U.S. Government has rights to use, reproduce, and distribute
 * this software.  NEITHER THE GOVERNMENT NOR Honeywell Federal Manufacturing & Technologies, LLC, MAKES ANY WARRANTY,
 * EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
 * derivative works, such modified software should be clearly marked, so as not to confuse it with the version available
 * from FM&T.
 *
 * Additionally, this program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License Version 3 published by the Free Software Foundation. Accordingly, this program is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this library.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.braintrain.domain;

import org.braintrain.domain.types.NavigationType;
import org.braintrain.services.util.contentBaseVisitor.ContentBaseResolverVisitor;
import org.braintrain.services.util.contentBaseVisitor.ContentBaseTestDataVisitor;
import org.braintrain.services.util.contentBaseVisitor.ContentBaseVisitorEditExtractor;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

/**
 * The heart of BrainTrain.  As the name suggests, this object is used to manage the
 * navigation though the content (ContentBase).  For easy insertions and traversing
 * from one item to another, a linked list approach was used.  This item could be 
 * considered the "link."  This approach depends heavily on the functions provided in the
 * NavigationService and NavigationDAO as well as the various Criteria and Query factories
 * in the search package.  
 * 
 * As in the Module class, deltaUsers and regular users must co-exists
 * and thus, the deltaNext, deltaPrev, and delta (ContentBase) objects are used to store the
 * Delta-User's changes.
 * 
 * Also, for a convinience, the moved, deleted, and pending booleans are used to store user
 * actions (makes for easy icon selection in the ModuleAdmin page).
 * 
 * 
 * @author John Ericksen
 *
 */

@Entity
@Table( name="braintrain_navigation" )
public class Navigation extends BrainTrainBase implements Comparable<Navigation>{

    private static final long serialVersionUID = -5978062530020974034L;
    
    private String name;
    
    private Integer navIndex;
    private Integer navIndexOutOf;
    
    private DeltaNavigationBundle bundle = new DeltaNavigationBundle();
    
    private DeltaContentBase content = new DeltaContentBase();
    
    private boolean pending = true;
    private boolean moved = false;
    private boolean deleted = false;

    private NavigationType type;

    private Module module;
    
    private boolean mainNavigation;
    
    private Set<ResourceReference> references = new HashSet<ResourceReference>();

    @OneToMany(mappedBy="navigationReference", targetEntity=ResourceReference.class, fetch=FetchType.LAZY)
    public Set<ResourceReference> getReferences() {
        return references;
    }

    public void setReferences(Set<ResourceReference> references) {
        this.references = references;
    }

    public boolean isMainNavigation() {
        return mainNavigation;
    }

    public void setMainNavigation(boolean mainNavigation) {
        this.mainNavigation = mainNavigation;
    }

    @ManyToOne(fetch=FetchType.LAZY, optional=false)
    @JoinColumn(name="module_id") 
    public Module getModule() {
        return module;
    }

    public void setModule(Module module) {
        this.module = module;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public Integer getNavIndex() {
        return navIndex;
    }

    public void setNavIndex(Integer navIndex) {
        this.navIndex = navIndex;
    }

    @Transient
    public boolean isTest()
    {
        return getType() == NavigationType.Test;
    }
    
    @Enumerated(EnumType.STRING)
    public NavigationType getType()
    {
        return type;
    }

    public void setType(NavigationType type) {
        this.type = type;
    }
    
    @Transient
    public boolean isBookmarked(User user) {
        ContentBaseTestDataVisitor visitor = new ContentBaseTestDataVisitor(this);
        return visitor.isTest() && visitor.getTest().isBookmarked(user);
    }
    
    @Transient
    public boolean isBookmarkable(){
        ContentBaseTestDataVisitor visitor = new ContentBaseTestDataVisitor(this);
        return visitor.isTest() && visitor.getTest().isBookmarkable();
    }

    public Integer getNavIndexOutOf() {
        return navIndexOutOf;
    }

    public void setNavIndexOutOf(Integer navIndexOutOf) {
        this.navIndexOutOf = navIndexOutOf;
    }

    @Transient
    public String getStatusString() {
        StringBuffer buff = new StringBuffer();
        
        if(isDeleted())
        {
            buff.append("Delete");
        }
        else if(isPending() || getNullSafeContent().getDeltaValue() != null)
        {
            buff.append("Edited");
        }
        else
        {
            buff.append("Released");
        }
        
        if(isMoved())
        {
            buff.append(" and Moved");
        }
        return buff.toString();
    }

    public boolean isDeleted() {
        return deleted;
    }

    public void setDeleted(boolean deleted) {
        this.deleted = deleted;
    }
    
    public boolean isPending() {
        return pending;
    }

    public void setPending(boolean pending) {
        this.pending = pending;
    }
    
    public boolean isMoved() {
        return moved;
    }

    public void setMoved(boolean moved) {
        this.moved = moved;
    }
    
    @Embedded
    public DeltaContentBase getContent() {
        return content;
    }
    public void setContent(DeltaContentBase content) {
        this.content = content;
    }
    @Transient
    public synchronized DeltaContentBase getNullSafeContent(){
        if(content == null){
            content = new DeltaContentBase();
        }
        return content;
    }
    
    @Transient
    public ContentBase getContent(boolean deltaUser)
    {
        return new ContentBaseResolverVisitor(this, deltaUser).getContent();
    }

    @Transient
    public ContentBase getEditableContent() {
        return new ContentBaseVisitorEditExtractor(this).getContent();
    }
    
    @Transient
    public String toString()
    {
        return getType().name() + " : " + getName();
    }

    public int compareTo(Navigation nav) {
        String thisName = this.getName();
        String thatName = nav.getName();
        
        if(thisName == null)
        {
            thisName = "Unnamed";
        }
        if(thatName == null)
        {
            thatName = "Unnamed";
        }
        
        return thisName.toLowerCase().compareTo(thatName.toLowerCase());
    }

    @Embedded
    public DeltaNavigationBundle getBundle() {
        return bundle;
    }

    public void setBundle(DeltaNavigationBundle bundle) {
        this.bundle = bundle;
    }
    @Transient
    public synchronized DeltaNavigationBundle getNullSafeBundle(){
        if(bundle == null){
            bundle = new DeltaNavigationBundle();
        }
        return bundle;
    }
    
    @Transient
    public Navigation getNext(){
        return getNullSafeBundle().getNext();
    }
    
    @Transient
    public void setNext(Navigation next){
        getNullSafeBundle().getValue().setNext(next);
    }
    
    @Transient
    public Navigation getDeltaNext(){
        return getNullSafeBundle().getDeltaNext();
    }
    
    @Transient
    public void setDeltaNext(Navigation next){
        getNullSafeBundle().getDeltaValue().setNext(next);
    }
    
    @Transient
    public Navigation getPrev(){
        return getNullSafeBundle().getPrev();
    }
    
    @Transient
    public Navigation getDeltaPrev(){
        return getNullSafeBundle().getDeltaPrev();
    }
}
